from torch import nn
import torch.utils.model_zoo as model_zoo
from models.resnext_features.resnext101_64x4d_features import resnext101_64x4d_features
from models.resnext_features.resnext101_32x4d_features import resnext101_32x4d_features
from functools import partial
from other_layers import *
__all__ = ['ResNeXt101_64x4d']

pretrained_settings = {
    'resnext101_32x4d': {
        'imagenet': {
            'url': 'http://data.lip6.fr/cadene/pretrainedmodels/resnext101_32x4d-29e315fa.pth',
            'input_space': 'RGB',
            'input_size': [3, 224, 224],
            'input_range': [0, 1],
            'mean': [0.485, 0.456, 0.406],
            'std': [0.229, 0.224, 0.225],
            'num_classes': 1000
        }
    },
    'resnext101_64x4d': {
        'imagenet': {
            'url': 'http://data.lip6.fr/cadene/pretrainedmodels/resnext101_64x4d-e77a0586.pth',
            'input_space': 'RGB',
            'input_size': [3, 224, 224],
            'input_range': [0, 1],
            'mean': [0.485, 0.456, 0.406],
            'std': [0.229, 0.224, 0.225],
            'num_classes': 1000
        }
    }
}

class ResNeXt101_32x4d(nn.Module):

    def __init__(self, num_classes=1000):
        super(ResNeXt101_32x4d, self).__init__()
        self.num_classes = num_classes
        self.features = resnext101_32x4d_features
        self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))
        self.last_linear = nn.Linear(2048, num_classes)

    def logits(self, input):
        x = self.avg_pool(input)
        x = x.view(x.size(0), -1)
        x = self.last_linear(x)
        return x

    def forward(self, input):
        x = self.features(input)
        x = self.logits(x)
        return x


class ResNeXt101_64x4d(nn.Module):

    def __init__(self, num_classes=1000):
        super(ResNeXt101_64x4d, self).__init__()
        self.num_classes = num_classes
        self.features = resnext101_64x4d_features
        self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))
        self.last_linear = nn.Linear(2048, num_classes)

    def logits(self, input):
        x = self.avg_pool(input)
        x = x.view(x.size(0), -1)
        x = self.last_linear(x)
        return x

    def forward(self, input):
        x = self.features(input)
        x = self.logits(x)
        return x


def resnext101_32x4d(num_classes=1000, pretrained='imagenet'):
    model = ResNeXt101_32x4d(num_classes=num_classes)
    if pretrained is not None:
        settings = pretrained_settings['resnext101_32x4d'][pretrained]
        assert num_classes == settings['num_classes'], \
            "num_classes should be {}, but is {}".format(settings['num_classes'], num_classes)
        model.load_state_dict(model_zoo.load_url(settings['url']))
        model.input_space = settings['input_space']
        model.input_size = settings['input_size']
        model.input_range = settings['input_range']
        model.mean = settings['mean']
        model.std = settings['std']
    return model
#
def resnext101_64x4d(num_classes=1000, pretrained='imagenet'):
    model = ResNeXt101_64x4d(num_classes=num_classes)
    if pretrained is not None:
        settings = pretrained_settings['resnext101_64x4d'][pretrained]
        assert num_classes == settings['num_classes'], \
            "num_classes should be {}, but is {}".format(settings['num_classes'], num_classes)

        model.load_state_dict(model_zoo.load_url(settings['url']))
        model.input_space = settings['input_space']
        model.input_size = settings['input_size']
        model.input_range = settings['input_range']
        model.mean = settings['mean']
        model.std = settings['std']
    return model

def _nostride_dilate( m, dilate):
    classname = m.__class__.__name__
    if classname.find('Conv') != -1:
        # the convolution with stride
        if m.stride == (2, 2):
            m.stride = (1, 1)
            if m.kernel_size == (3, 3):
                m.dilation = (dilate//2, dilate//2)
                m.padding = (dilate//2, dilate//2)
        # other convoluions
        else:
            if m.kernel_size == (3, 3):
                m.dilation = (dilate, dilate)
                m.padding = (dilate, dilate)


def resnext101_64_cat_dilated(num_classes,pretrained=True):
    base_model = resnext101_64x4d()
    base_model.features[6].apply(partial(_nostride_dilate, dilate=2))
    base_model.features[7].apply(partial(_nostride_dilate, dilate=4))
    base_model.last_linear = cat_linear(2048, num_classes)

    return base_model


def resnext101_32_cat_dilated(num_classes,pretrained=True):
    base_model = resnext101_32x4d()
    base_model.features[6].apply(partial(_nostride_dilate, dilate=2))
    base_model.features[7].apply(partial(_nostride_dilate, dilate=4))
    base_model.last_linear = cat_linear(2048, num_classes)

    return base_model


if __name__ == '__main__':
    model = resnext101_32_cat_dilated([10,5])
    import torch
    print model

    x = torch.FloatTensor(3,3,336,336).zero_()
    x = torch.autograd.Variable(x)
    # print model.features
    y = model(x)
    print y